/*
 *  linux/fs/file_dev.c
 *
 *  (C) 1991  Linus Torvalds
 */

#include <errno.h>          // 错误号头文件 包含系统中各种出错号 （Linus 从 MINIX中引进的）
#include <fcntl.h>          // 文件控制头文件 用于文件及其描述符的操作控制常数符号的定义

#include <linux/sched.h>    // 调度程序头文件 定义了任务结构task_struct 初始任务0的数据
#include <linux/kernel.h>   // 内核头文件 含有一些内核常用函数的原形定义
#include <asm/segment.h>    // 段操作头文件 定义了有关段寄存器操作的嵌入式汇编函数

#define MIN(a,b) (((a)<(b))?(a):(b))    // 取 a b 中最小值
#define MAX(a,b) (((a)>(b))?(a):(b))    // 取 a b 中最大值
// 文件读函数-根据i节点和文件结构 读设备数据 由i节点可以知道设备号 由filp结构可以知道文件中当前读写指针位置 buf指定用户态中缓冲区的位置 count为需要读取的字节数 返回值是实际读取的字节数 或出错号（小于0）
int file_read(struct m_inode * inode, struct file * filp, char * buf, int count)
{
	int left,chars,nr;
	struct buffer_head * bh;
    // 若需要读取的字节计数值小于等于零 则返回
	if ((left=count)<=0)
		return 0;
	while (left) {  // 若还需要读取的字节数不等于0 就循环执行以下操作 知道全部独处
		if (nr = bmap(inode,(filp->f_pos)/BLOCK_SIZE)) {    // 根据i节点和文件表结构信息 取数据块文件当前读写位置在设备上对应的逻辑块号nr 若nr不为0 则从i节点指定的设备上读取该逻辑块 如果读操作失败则退出循环 若nr为0 表示指定的数据块不存在 置缓冲块指针为NULL
			if (!(bh=bread(inode->i_dev,nr)))
				break;
		} else
			bh = NULL;
		nr = filp->f_pos % BLOCK_SIZE;  // 计算文件读写指针在数据块中的偏移值nr 则该块中可读字节数为（BLOCK_SIZE-nr） 然后与还需读取的字节数left作比较 其中小值即为本次需读的字节数chars 若（BLOCK_SIZE-nr）大则说明该块是需要读取的最后一块数据 反之还需要读取一块数据
		chars = MIN( BLOCK_SIZE-nr , left );
		filp->f_pos += chars;   // 调整读写文件指针 指针前移此次将读取的额字节数chars 剩余字节计数相应减去 chars
		left -= chars;
		if (bh) {   // 若从设备上读到了数据 则将p指向独处数据块缓冲区中开始读取的位置 并且复制chars字节到用户缓冲区buf中 否则往用户缓冲区中填入chars个0值字节
			char * p = nr + bh->b_data;
			while (chars-->0)
				put_fs_byte(*(p++),buf++);
			brelse(bh);
		} else {
			while (chars-->0)
				put_fs_byte(0,buf++);
		}
	}   // 修改该i节点的访问时间为当前时间 返回读取的字节数 若读取字节数为0 则返回出错号
	inode->i_atime = CURRENT_TIME;
	return (count-left)?(count-left):-ERROR;
}
// 文件写函数-根据i节点和文件结构信息 将用户数据写入指定设备 由i节点可以知道设备号 由filp结构可以知道文件中当前写指针位置 buf指定用户态中缓冲区的位置 count为需要写入的字节数 返回值是世界写入的字节数 或出错号（小于0）
int file_write(struct m_inode * inode, struct file * filp, char * buf, int count)
{
	off_t pos;
	int block,c;
	struct buffer_head * bh;
	char * p;
	int i=0;

/*  // 当许多进程同时写时 append 操作可能不行 但那又怎样 不管怎样 那样做会导致混乱一团
 * ok, append may not work when many processes are writing at the same time
 * but so what. That way leads to madness anyway.
 */ // 如果是要向文件后添加数据 则将文件读写指针移到文件尾部 否则就将在文件读写指针处写入
	if (filp->f_flags & O_APPEND)
		pos = inode->i_size;
	else
		pos = filp->f_pos;
	while (i<count) {   // 若已写入字节数i小于需要写入的数count 则循环执行以下操作 创建数据库块号（pos/BLOCK_SIZE） 在设备上对应的逻辑块 并返回在设备上的逻辑块号 若逻辑块号=0 则表示创建失败 退出循环
		if (!(block = create_block(inode,pos/BLOCK_SIZE)))
			break;  // 根据该逻辑块号读取设备上的相应数据块 若出错则退出循环
		if (!(bh=bread(inode->i_dev,block)))
			break;
		c = pos % BLOCK_SIZE;   // 求出文件读写指针在数据块中的偏移值c 将p指向独处数据块缓冲区中开始读取的位置 置该缓冲区已修改标志
		p = c + bh->b_data;
		bh->b_dirt = 1;
		c = BLOCK_SIZE-c;   // 从开始读写位置到块末共可写入c=（BLOCK_SIZE-c）个字节 若c大于剩余还需写入的字节数（count-i）则此次只需再写入c=（count-i）即可
		if (c > count-i) c = count-i;
		pos += c;           // 文件读写指针前移此次需写入的字节数 如果当前文件读写指针位置值超过了文件的大小 则修改i节点中文件大小字段 并置i节点已修改标志
		if (pos > inode->i_size) {
			inode->i_size = pos;
			inode->i_dirt = 1;
		}
		i += c; // 已写入字节技术累加此次写入的字节数c 从用户缓冲区buf中复制c个字节到高速缓冲区中p指向开始的伟指出 然后释放该缓冲区
		while (c-->0)
			*(p++) = get_fs_byte(buf++);
		brelse(bh);
	}
	inode->i_mtime = CURRENT_TIME;  // 更改文件修改时间为当前时间
	if (!(filp->f_flags & O_APPEND)) {  // 如果此次操作不是在文件尾添加数据 则把文件读写指针调整到当前读写位置 并更改i节点修改时间为当前时间
		filp->f_pos = pos;
		inode->i_ctime = CURRENT_TIME;
	}
	return (i?i:-1);    // 返回写入的字节数 若写入字节数为0 则返回出错号-1
}
